﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using System.Web.SessionState;
using System.Web.UI.WebControls;
using Image = System.Drawing.Image;

namespace Library.WebControls.SimpleCapcha {
	public class SpCapchaImageHandler : IHttpHandler, IRequiresSessionState {
		private HttpContext _context;

		private SpCapcha.CapchaInfo _info;



		public virtual void ProcessRequest(HttpContext context) {
			_context = context;
			var id = _context.Request["I"];
			_info = (SpCapcha.CapchaInfo)context.Session[SpCapcha.Key + id];
			if (_info == null) _info = new SpCapcha.CapchaInfo();

			if (_info.Height == 0 || _info.Width == 0) return;
			Text = CreateRandomText(context);
			Parameter = context.Request["P"];
			var img = GenerateImage();

			context.Response.ContentType = "image/jpg";
			var bs = new byte[0];
			using (var ms = new MemoryStream()) {
				img.Save(ms, ImageFormat.Jpeg);
				bs = ms.ToArray();
			}
			context.Response.OutputStream.Write(bs, 0, bs.Length);
			context.Response.Flush();

		}

		protected virtual string CreateRandomText(HttpContext context) {
			return new Random().Next().ToString();
		}

		public string Text {
			get {
				if (_info == null) return null;
				return _info.Text;
			}
			set {
				if (_info == null) return;
				_info.Text = value;
			}
		}

		public string Parameter { get; private set; }

		public string CapchaKey {
			get {
				if (_info == null) return null;
				return _info.CapchaKey;
			}

		}

		public virtual bool IsReusable {
			get { return false; }
		}

		private Image GenerateImage() {
			var random = new Random();
			// Create a new 32-bit bitmap image.
			var bitmap = new Bitmap(
				_info.Width,
				_info.Height,
				PixelFormat.Format32bppArgb);

			// Create a graphics object for drawing.
			Graphics g = Graphics.FromImage(bitmap);
			g.SmoothingMode = SmoothingMode.AntiAlias;
			var rect = new Rectangle(0, 0, _info.Width, _info.Height);

			// Fill in the background.
			var hatchBrush = new HatchBrush(
				HatchStyle.SmallConfetti,
				Color.LightGray,
				Color.White);
			g.FillRectangle(hatchBrush, rect);

			// Set up the text font.
			SizeF size;
			//	float fontSize = rect.Height + 1;
			Font font;
			bool first = true;
			// Adjust the font size until the text fits within the image.
			float fSize = (float)(_info.TextHeight);
			if (fSize == 0) fSize = rect.Height;
			const GraphicsUnit unit = GraphicsUnit.Pixel;

			do {
				if (!first) {
					fSize--;
				} else {
					first = false;
				}


				FontStyle style = FontStyle.Regular;


				if (_info.Font.Bold) style = style | FontStyle.Bold;
				if (_info.Font.Italic) style = style | FontStyle.Italic;
				if (_info.Font.Strikeout) style = style | FontStyle.Strikeout;
				if (_info.Font.Underline) style = style | FontStyle.Underline;
				font = new Font(_info.Font.Name, fSize, style, unit);


				size = g.MeasureString(_info.Text, font);
			} while (size.Width > rect.Width);

			// Set up the text format.
			var format = new StringFormat();
			format.Alignment = StringAlignment.Center;
			format.LineAlignment = StringAlignment.Center;

			// Create a path using the text and warp it randomly.
			var path = new GraphicsPath();
			path.AddString(
				_info.Text, new FontFamily(font.Name),
				(int)font.Style,
				font.Size, rect,
				format);
			float v = 4F;
			PointF[] points =
      {
        new PointF(
          random.Next(rect.Width) / v,
          random.Next(rect.Height) / v),
        new PointF(
          rect.Width - random.Next(rect.Width) / v,
          random.Next(rect.Height) / v),
        new PointF(
          random.Next(rect.Width) / v,
          rect.Height - random.Next(rect.Height) / v),
        new PointF(
          rect.Width - random.Next(rect.Width) / v,
          rect.Height - random.Next(rect.Height) / v)
      };
			var matrix = new Matrix();
			matrix.Translate(0F, 0F);
			path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);

			// Draw the text.
			hatchBrush = new HatchBrush(
				HatchStyle.LargeConfetti,
				Color.LightGray,
				Color.DarkGray);
			g.FillPath(hatchBrush, path);

			// Add some random noise.
			int m = Math.Max(rect.Width, rect.Height);
			for (int i = 0; i < (int)(rect.Width * rect.Height / 30F); i++) {
				int x = random.Next(rect.Width);
				int y = random.Next(rect.Height);
				int w = random.Next(m / 50);
				int h = random.Next(m / 50);
				g.FillEllipse(hatchBrush, x, y, w, h);
			}

			// Clean up.
			font.Dispose();
			hatchBrush.Dispose();
			g.Dispose();

			// Set the image.
			return bitmap;
		}

	}
}
